using System.Collections.Generic;
using Gameplay.PVE.Data;
using Gameplay.PVE.Entity;
using Gameplay.PVE.TurnBase;
using Gameplay.PVE.Utils;
using UnityEngine;
using UnityEngine.AI;
using BattleUnit = Gameplay.PVE.Entity.BattleUnit;

namespace Gameplay.PVE
{
    public class PveFindPathManager
    {
        private static PveFindPathManager instance;

        public static PveFindPathManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new PveFindPathManager();
                }
                return instance;
            }
        }
        private List<UnitBase> units = new List<UnitBase>();
        private List<TurnBase.BattleUnit> tbsUnits = new List<TurnBase.BattleUnit>();
        private List<AStarMgr.Obstacle> obstacles = new List<AStarMgr.Obstacle>();
        private int currentIndex = 0;
        private Dictionary<int, PveRoadPath> roads = new Dictionary<int, PveRoadPath>();
        List<NavMeshBuildSource> m_Sources = new List<NavMeshBuildSource>();
        private AStarMgr astarMgr;
        private SmoothModifier smoothModifier;
        public void Initialize()
        {
            //InitRoadPath();
        }

        public void Dispose()
        {
            units?.Clear();
            roads?.Clear();
            astarMgr?.Dispose();
            instance = null;
        }

        public void InitAstarMap(Vector3 offset)
        {
            astarMgr = new AStarMgr();
            astarMgr.InitMapInfo(50, 36,offset);
            smoothModifier = new SmoothModifier();
        }

        public void UpdateOffset(Vector3 offset)
        {
            astarMgr?.UpdateOffset(offset);
        }

        public void ToNext(int stageId)
        {
            astarMgr?.ToNext(stageId);
        }

        public void ClearStage()
        {
            units.Clear();
            astarMgr?.ClearStage();
        }
        

        private Bounds bound;
        //private float navMeshUpdateTime;
        
        /*public void UpdateNavMeshPlane(Vector3 position)
        {
            navMeshPlane.transform.position = position;
            //navMeshUpdateTime = TimeManager.logicTime;
            bound = QuantizedBounds(position);
            NavMeshSourceTag.Collect(ref m_Sources);
            var defaultBuildSettings = NavMesh.GetSettingsByID(0);
            defaultBuildSettings.agentHeight = 0;
            NavMeshBuilder.UpdateNavMeshData(m_NavMesh, defaultBuildSettings, m_Sources,bound);
            navMeshPlane.SetActive(false);
        }
        
        Vector3 m_Size = new Vector3(5000,100,5000);
        
        private Bounds QuantizedBounds(Vector3 center)
        {
            return new Bounds(Quantize(center, 0.1f * m_Size), m_Size);
        }
        
        private static Vector3 Quantize(Vector3 v, Vector3 quant)
        {
            float x = quant.x * Mathf.Floor(v.x / quant.x);
            float y = quant.y * Mathf.Floor(v.y / quant.y);
            float z = quant.z * Mathf.Floor(v.z / quant.z);
            return new Vector3(x, y, z);
        }*/
        
        private void InitRoadPath()
        {
            var path = new PveRoadPath();
            path.InitData(0);
            roads.Add(0,path);
            
            path = new PveRoadPath();
            path.InitData(1);
            roads.Add(1,path);
        }
        

        public void Update()
        {
            if (astarMgr != null)
            {
                if (PveScene.IsToTurnBase)
                {
                    
                }
                else// if (PveManager.Instance.isInRpg)
                {
                    //if (PveManager.Instance.isInRpg)
                    {
                        for (int i = 0; i < units.Count; i++)
                        {
                            astarMgr.UpdateObstaclePosition(units[i].Data.id,units[i].Data.astarPosition,units[i].Data.attackPosition);
                        }
                        astarMgr.UpdateMapCollider();
                    }
                }
            }
        }

        public void UpdateSingleCollider(int id,Vector3 position)
        {
            astarMgr.UpdateObstaclePosition(id,position,Vector3.zero);
        }

        public void DrawBound()
        {
            if (bound == null)
            {
                return;
            }
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireCube(bound.center, bound.size);
        }

        public bool IsSameGrid(Vector3 a,Vector3 b)
        {
            return astarMgr.IsSameGrid(a, b);
        }

        public Vector3 GetPointByObstacle(int id, Vector3 start, Vector3 end)
        {
            var list = new List<Vector3>();
            var isSuccess = astarMgr.FindPath(ref list,id,start, end);
            return list[list.Count - 1];
        }

        public bool GetPathByObstacle(ref List<Vector3> list,int id,Vector3 start, Vector3 end,bool forceEnd = false)
        {
            list.Clear();
            if (PveGlobalVlues.DisableAStar)//暂时取消寻路
            {
                list.Add(start);
                list.Add(end);
                return true;
            }
            var isSuccess = astarMgr.FindPath(ref list, id,start, end);
            if (forceEnd && list.Count > 0 && list[list.Count - 1] != end)
            {
                list.Add(end);
            }

            var smoothList = smoothModifier.Smooth(list);
            list = smoothList;
            return isSuccess;
        }
        
        public Vector3 FindAttackPosition(BattleUnit attackerUnit,BattleUnit targetUnit,float attackRange)
        {
            Vector3 position = targetUnit.Data.position;
            int intervals = 12;
            float singleAngle = 360f / intervals;
            Vector3 initPosition = (attackerUnit.Data.position - targetUnit.Data.position).normalized * attackRange;
            bool hasFind = false;
            float distance = attackRange * 2 * 3.14f / intervals / 2;
            for (int i = 0; i < intervals / 2; i++)
            {
                float angle = -i * singleAngle;
                position = Quaternion.AngleAxis(angle, Vector3.up) * initPosition + targetUnit.Data.position;
                bool valid = CheckIsPointValid(attackerUnit, targetUnit, position,distance);
                if (valid)
                {
                    hasFind = true;
                    break;
                }
                
                position = Quaternion.AngleAxis(-angle, Vector3.up) * initPosition + targetUnit.Data.position;
                valid = CheckIsPointValid(attackerUnit, targetUnit, position,distance);
                if (valid)
                {
                    hasFind = true;
                    break;
                }
            }

            if (!hasFind)
            {
                position = initPosition + targetUnit.Data.position;
            }
            
            return position;
        }
        
        //判断坐标是否有重叠
        private bool CheckIsPointValid(BattleUnit attackerUnit,BattleUnit targetUnit,Vector3 position,float minDistance)
        {
            for (int i = 0; i < units.Count; i++)
            {
                var item = units[i];
                if (item == attackerUnit || item == targetUnit)
                {
                    continue;
                }

                float distance = Vector3.Distance(item.Data.attackPosition, position);
                
                if (distance < minDistance)
                {
                    return false;
                }
            }

            /*agent.Warp(attackerUnit.Data.position);
            if (PveManager.Instance.isInRpg)
            {
                var isValid = agent.CalculatePath(position, navMeshPath);
                if (!isValid || navMeshPath.corners.Length>0 && !IsSamePosition(navMeshPath.corners[navMeshPath.corners.Length - 1],position))
                {
                    return false;
                }
            }*/
            return true;
        }

        private bool IsSamePosition(Vector3 a, Vector3 b)
        {
            return Mathf.Abs(a.x - b.x) <= 0.001 && Mathf.Abs(a.z - b.z) <= 0.001;
        }

        public List<Vector3> GetMortalPath(Vector3 currentPosition,Vector3 stagePosition)
        {
            /*PveRoadPath path;
            if (roads.TryGetValue(0, out path))
            {
                var list = path.GetRandomPathByStartPoint(currentPosition - stagePosition);
                for (int i = 0; i < list.Count; i++)
                {
                    Vector3 randomOffset = Vector3.zero;
                    if (i != 0)
                    {
                        randomOffset = new Vector3(Random.Range(-1f,1f),0,Random.Range(-1f,1f));
                    }
                    list[i] += stagePosition + randomOffset;
                }

                return list;
            }*/
            return new List<Vector3>();
        }

        private List<int> VehiclePointStartList = new List<int>
        {
            5,7,9,11,
        };
        private List<int> VehiclePointEndList = new List<int>
        {
            4,6,8,10,
        };

        public List<Vector3> GetVehiclePath(Vector3 stagePosition)
        {
            PveRoadPath path;
            if (roads.TryGetValue(1, out path))
            {
                int start = VehiclePointStartList[Random.Range(0, VehiclePointStartList.Count)];
                int end = VehiclePointEndList[Random.Range(0, VehiclePointEndList.Count)];
                var list = path.GetPath(start, end,stagePosition);
                //针对转弯进行特殊处理
                for (int i = 0; i < list.Count - 2; i++)
                {
                    Vector3 dir1 = list[i] - list[i + 1];
                    Vector3 dir2 = list[i + 2] - list[i + 1];
                    float angle = PveUtils.GetAngle(dir1, dir2);
                    float angleAbs = Mathf.Abs(angle);
                    float radius = 1;
                    if (angleAbs < 150)//把中间一个点转换成n个点
                    {
                        //先算出圆心到拐弯角的距离
                        float distance = radius / Mathf.Sin(angleAbs / 2 * Mathf.Deg2Rad);
                        
                        //然后旋转得到方向向量
                        Vector3 v = Quaternion.AngleAxis(angle / 2,Vector3.up) * dir1.normalized;
                        
                        //然后求出圆心
                        Vector3 circleCenter = list[i + 1] + v * distance;
                        //然后补齐点
                        int pointCount = 20;
                        float angleInterval = angle / 2 / pointCount;
                        List<Vector3> points = new List<Vector3>();
                        points.Add(circleCenter - v * radius);
                        for (int j = 1; j < pointCount; j++)
                        {
                            points.Add(Quaternion.AngleAxis(-angleInterval * j,Vector3.up) *  - v * radius + circleCenter);
                            points.Insert(0,Quaternion.AngleAxis(angleInterval * j,Vector3.up) *  - v * radius + circleCenter);
                        }
                        list.RemoveAt(i+1);
                        int currentIndex = i + 1;
                        for (int j = 0; j < points.Count; j++)
                        {
                            list.Insert(currentIndex,points[j]);
                            currentIndex++;
                        }
                        i += currentIndex;
                    }
                }

                
                
                return list;
            }
            return new List<Vector3>();
        }
        
        public void AddUnit(UnitBase unit)
        {
            int id = unit.Data.id;
            /*var obj = new GameObject("obstacle " + id);
            var obstacle = obj.AddComponent<NavMeshObstacle>();
            obj.transform.SetParent(obj.transform);
            obstacle.carving = true;
            obstacle.shape = NavMeshObstacleShape.Capsule;
            obstacle.radius = 0.5f * PveUtils.globalRatio;
            obstacles.Add(unit.Data.id,obj);*/
            units.Add(unit);
            if (unit is BattleUnit)
            {
                int width = 2;
                int height = 2;
                /*if (unit.Data.team == 2)
                {
                    width = 0;
                    height = 0;
                }*/
                var blockType = AStarBlockType.Ground;
                if (unit.Data.modelId == PveGlobalVlues.AirDroneModelId || unit.Data.modelId == PveGlobalVlues.HelicopterModelId)
                {
                    blockType = AStarBlockType.Air;
                }
                astarMgr.AddObstacle(unit.Data.id, unit.Data.position, width, height,blockType);
            }
            else
            {
                astarMgr.AddObstacle(unit.Data.id, unit.Data.position, 4, 4);
            }
            
        }

        public void RemoveUnit(UnitBase unit)
        {
            /*GameObject obj;
            if (obstacles.TryGetValue(unit.Data.id, out obj))
            {
                GameObject.DestroyImmediate(obj);
                obstacles.Remove(unit.Data.id);
            }*/
            units.Remove(unit);
            astarMgr?.RemoveObstacle(unit.Data.id);   
        }

        public void AddUnit(TurnBase.BattleUnit unit)
        {
            tbsUnits.Add(unit);
            astarMgr.AddObstacle(unit.Data.id, unit.Data.position, 4, 4);
        }

        public void RemoveUnit(TurnBase.BattleUnit unit)
        {
            tbsUnits.Remove(unit);
            astarMgr.RemoveObstacle(unit.Data.id);   
        }

        public void AddObstacle(int id,Vector3 position,RpgLevelData.RpgObstacle obstacle,int stageId)
        {
            astarMgr.AddStaticObstacle(id, position, obstacle,stageId);
        }

        public void AddObstacle(int id, Vector3 position, Vector3 size)
        {
            astarMgr.AddObstacle(id,position,size);
        }

        public void RemoveObstacle(int id)
        {
            astarMgr.RemoveObstacle(id);
        }

        public void AddStaticBlocks(List<List<bool>> blocks,Vector3 position)
        {
            astarMgr.AddStaticBlocks(blocks,position);
        }
        
        public bool CheckIsClear(Vector3 position,int width,int height)
        {
            return astarMgr.CheckIsClear(position,width,height);
        }

        public bool FindValidPosition(ref Vector3 position, int width, int height, int range = 5,PveTargetSearchHelper.RegionAdapter regionAdapter = null)
        {
            return astarMgr.FindValidPosition(ref position, width, height,range,regionAdapter);
        }

        public bool CheckIsWalkable(Vector3 position, int id,AStarBlockType blockType = AStarBlockType.All)
        {
            return astarMgr.IsWalkable(position, id,blockType);
        }

        public bool CheckIsCanWalkForward(Vector3 position, Vector3 forward, int id)
        {
            return astarMgr.IsCanWalkForward(position, forward, id);
        }

        public bool CheckIsOutOfBound(Vector3 position)
        {
            if (PveManager.Instance.isInPve || PveManager.Instance.isToRpg)
            {
                return false;
            }
            bool isOut = false;
            isOut = isOut || astarMgr.IsOutOfBound(position);
            return isOut;
        }

        public bool GetFirstAvailablePosition(ref Vector3 position,Vector3 forward,int id)
        {
            return astarMgr.GetFirstAvailablePosition(ref position, forward, id);
        }
    }
}